// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using SnapObjects.Commons;

namespace SnapObjects.Data
{
    /// <summary>
    ///     Specifies that when there is an insert or update operation in the master model, 
    ///     set the property value of the master model to the associated property of the detail model, 
    ///     when working with the master-detail models. One property is allowed to apply multiple SetValue attributes.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
    public class SetValueAttribute : Attribute, ISetValue
    {
        /// <summary>
        ///     Initializes a new instance of the SetValueAttribute class.
        /// </summary>
        /// <param name="source">
        ///     A property of the model class at the same level.
        ///     Use this format to make reference: $PropertyName, for example, $Id.
        /// </param>
        /// <param name="target">
        ///     A property of the model class at one level lower.
        ///     Use this format to make reference: $PropertyName, for example, $Id.
        /// </param>
        public SetValueAttribute(
            string source,
            string target)
            : this(source, target, SetValueStrategy.Always, ModelSelector.All)
        {
        }

        /// <summary>
        ///     Initializes a new instance of the SetValueAttribute class.
        /// </summary>
        /// <param name="source">
        ///     A property of the model class at the same level.
        ///     Use this format to make reference: $PropertyName, for example, $Id.
        /// </param>
        /// <param name="target">
        ///     A property of the model class at one level lower.
        ///     Use this format to make reference: $PropertyName, for example, $Id.
        /// </param>
        /// <param name="setValueStrategy">
        ///     The default is SetValueStrategy.Always which indicates values
        ///     should always be set for the specified property in the embedded model whenever there is an 
        ///     insert or update operation in the model at the same level.
        /// </param>
        public SetValueAttribute(
            string source,
            string target,
            SetValueStrategy setValueStrategy)
            : this(source, target, setValueStrategy, ModelSelector.All)
        {
        }

        /// <summary>
        ///     Initializes a new instance of the SetValueAttribute class.
        /// </summary>
        /// <param name="source">
        ///     A property of the model class at the same level.
        ///     Use this format to make reference: $PropertyName, for example, $Id.
        /// </param>
        /// <param name="target">
        ///     A property of the model class at one level lower.
        ///     Use this format to make reference: $PropertyName, for example, $Id.
        /// </param>
        /// <param name="setValueStrategy">
        ///     The default is SetValueStrategy.Always which indicates values
        ///     should always be set for the specified property in the embedded model 
        ///     whenever there is an insert or update operation in the model at the same level.
        /// </param>
        /// <param name="modelSelector">
        ///     The default is ModelSelector.All which indicates values should be set 
        ///     no matter whether the data has been updated or not in the embedded model.
        /// </param>
        public SetValueAttribute(
            string source,
            string target,
            SetValueStrategy setValueStrategy,
            ModelSelector modelSelector)
        {
            this.Source = source;
            this.Target = target;
            this.SetValueStrategy = setValueStrategy;
            this.ModelSelector = modelSelector;

            Check.NotEmpty(this.Source, nameof(this.Source));
            Check.NotEmpty(this.Target, nameof(this.Target));
        }

        /// <summary>
        ///     Gets the string that represents a property in the model class at the same level.
        /// </summary>
        public string Source { get; }

        /// <summary>
        ///     Gets the string that represents a property in the model class at one level lower.
        /// </summary>
        public string Target { get; }

        /// <summary>
        ///     Gets a SetValueStrategy enumerated value which determines when to set value.
        /// </summary>
        public SetValueStrategy SetValueStrategy { get; }

        /// <summary>
        ///     Gets a ModelSelector enumerated value which determines whether to set 
        ///     value by the data state in the embedded model.
        /// </summary>
        public ModelSelector ModelSelector { get; }
    }
}
